package Dungeoneering.ActionHandlers;

import java.awt.Point;

import org.powerbot.game.api.methods.Calculations;
import org.powerbot.game.api.methods.Game;
import org.powerbot.game.api.methods.input.Mouse;
import org.powerbot.game.api.methods.interactive.Players;
import org.powerbot.game.api.methods.node.Menu;
import org.powerbot.game.api.util.Filter;
import org.powerbot.game.api.util.Timer;
import org.powerbot.game.api.wrappers.Entity;
import org.powerbot.game.api.wrappers.Locatable;
import org.powerbot.game.api.wrappers.Tile;
import org.powerbot.game.api.wrappers.ViewportEntity;
import org.powerbot.game.api.wrappers.interactive.NPC;
import org.powerbot.game.api.wrappers.node.GroundItem;
import org.powerbot.game.api.wrappers.node.SceneObject;
import org.powerbot.game.api.wrappers.widget.WidgetChild;

import Dungeoneering.Utils.Utilities;

/**
 * The MouseHandler will do all actions that have to do with Mouse input
 * @author Nicolaas
 *
 */
class MouseHandler { // package protected
	// TODO : TEST IF MAINTHREAD ACTUALLY GETS STALLED!!!!!!!!!!
	private static final MouseHandler INSTANCE = new MouseHandler();
	
	private MouseThread mouseThread;

	private MouseHandler() {
		
	}
	
	public static MouseHandler getInstance() {
		return INSTANCE;
	}
	
	public void clickFirstOption(Entity e) {
		if (!e.validate()) {
			System.out.println("not valid");
			return;
		}
		
		while (mouseThread != null && mouseThread.isAlive())
			Utilities.sleep(5);
		if (e instanceof GroundItem)
			mouseThread = new MouseThread(e, "Take", ((GroundItem) e).getGroundItem().getName(), false);
		else if (e instanceof SceneObject)
			mouseThread = new MouseThread(e, ((SceneObject) e).getDefinition().getActions()[0], ((SceneObject) e).getDefinition().getName(), true);
		else if (e instanceof NPC)
			mouseThread = new MouseThread(e,((NPC) e).getActions()[0], ((NPC) e).getName(), true);
		else if (e instanceof WidgetChild)
			mouseThread = new MouseThread((WidgetChild) e);
		mouseThread.start();
	}

	private class MouseThread extends Thread {
		
		private ViewportEntity entity;
		private String menuAction;
		private String menuOption;
		private boolean tryLeft;
		private boolean preAim;
		
		private MouseThread(ViewportEntity entity, String menuAction, String menuOption, boolean tryLeft) {
			this.entity = entity;
			this.menuAction = menuAction;
			this.menuOption = menuOption;
			this.tryLeft = tryLeft;
			this.preAim = false;
		}
		
		private MouseThread(WidgetChild widget) {
			this.preAim = true;
			entity = widget;
		}
		
	    private Point customGroundToScreen(int x, int z, final int height) {
	    	x = (int)((double)(x - Game.getBaseX() + 0.5) * 512);
	    	z = (int)((double)(z - Game.getBaseY() + 0.5) * 512);
	    	int y = Calculations.calculateTileHeight(x, z, Game.getPlane()) + height;
	        return Calculations.worldToScreen(x, y, z);
	    }
	    
	    private boolean openMenu() {
	    	Utilities.thinkSleep();
	    	Mouse.click(false);
			Timer t = new Timer(300);
			while (!Menu.isOpen() && t.isRunning()) {
				try {
					sleep(10);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
			return t.isRunning();
	    }
	    
	    private double distanceTo(Tile tile) {
			return Calculations.distance(Players.getLocal().getLocation(), tile);
		}

		@Override
		public void run() {
			System.out.println("Started mouseThread");
			// TODO : timeout listener!!!!
			final Timer timer = new Timer(5000);
			setPriority(MAX_PRIORITY);
			if (entity instanceof WidgetChild) {
				((WidgetChild) entity).hover();
				return;
			}
			int pitch = (int)(100 - (10 * distanceTo((entity instanceof Tile ? (Tile)entity : ((Locatable) entity).getLocation()))));
			CameraHandler.getInstance().turnTo((Entity)entity, pitch >= 0 ? pitch : 0);
			if (entity instanceof GroundItem) {
				((GroundItem) entity).hover();
				if (Menu.contains(menuAction, menuOption)) {
					if (openMenu())
						Menu.select(menuAction, menuOption);
					return;
				}
				Tile t = ((Locatable)entity).getLocation();
				Point p = customGroundToScreen(t.getX(), t.getY(), -400);
				if (p.x != 0 || p.y != 0) {
					Mouse.move(p.x, p.y, 5, 5);
					if (Menu.contains(menuAction, menuOption)) {
						if (openMenu())
							Menu.select(menuAction, menuOption);
						return;
					}
				}
			} else {
				if (entity instanceof SceneObject) {
					((SceneObject) entity).hover();
				} else if (entity instanceof NPC) {
					((NPC) entity).hover();
				}
				Mouse.apply(entity, new Filter<Point>() { // TODO check whether Mouse.apply clicks :/
	
					@Override
					public boolean accept(Point p) {
						System.out.println("teehee");
						if (Menu.contains(menuAction, menuOption)) {
							if (tryLeft) {
								// TODO : implement detection of locations/npcs' that might get missclicked on FOR NOW we right click on everything
								//if (openMenu()) {
								//	return Menu.select(menuAction, menuOption);
							//	}
							//	Mouse.click(true);
								return true;
							} else {
								//if (openMenu()) {
								//	return Menu.select(menuAction, menuOption);
								//}
								return true;
							}
							//return false;
						}
						if (!timer.isRunning()) {
							System.out.println("mousethread timed out");
							// TODO : screen capture!!!
							return true;
						}
						return false;
					}
				});
				System.out.println("testghdghiudgh");
			}
			Utilities.thinkSleep();
			System.out.println("Ended MouseThread");
		}
	}
}
